package com.vhall.component.service.sign.service.impl;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.vhall.component.dao.sign.SignMapper;
import com.vhall.component.dao.sign.SignRecordMapper;
import com.vhall.component.entity.room.dto.RoomJoinsEntityDTO;
import com.vhall.component.entity.room.dto.RoomsEntityDTO;
import com.vhall.component.entity.sign.Sign;
import com.vhall.component.entity.sign.SignRecord;
import com.vhall.component.entity.sign.vo.*;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.framework.common.exception.GlobalErrorCode;
import com.vhall.component.plugin.common.constant.RedisKey;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.plugin.common.support.VssToken;
import com.vhall.component.plugin.common.support.VssTokenThreadLocal;
import com.vhall.component.plugin.core.service.PaasService;
import com.vhall.component.service.room.RoomInnerService;
import com.vhall.component.service.sign.constant.SignConstant;
import com.vhall.component.service.sign.service.SignService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.vhall.component.service.sign.constant.SignConstant.DEFAULT_VALUE_EXPIRED_TIME;

/**
 * @author shenqing
 * @date 2021/4/22 19:39
 */
@Service
@Slf4j
public class SignServiceImpl implements SignService {

    @Autowired
    private PaasService paasService;

    @Autowired
    private SignMapper signMapper;

    @Autowired
    private RoomInnerService roomInnerFacade;


    @Value("${paas.apps.lite.appId}")
    public String appId;
    @Value("${paas.apps.lite.appSecret}")
    public String appSecret;

    @Autowired
    private SignRecordMapper signRecordMapper;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private static final String CREATED_AT_COLUMN = "created_at";

    private static final String ROOM_ID_COLUMN = "room_id";

    /**
     * 发起签到
     *
     * @param reqVO
     * @return
     */
    @Override
    public SignDTO add(SignAddToVpsReqVO reqVO) {

        SignDTO signDTO = new SignDTO();
        VssToken vssToken = VssTokenThreadLocal.getToken();
        Integer thirdId = vssToken.getThirdPartyUserId();
        RoomJoinsEntityDTO info = getRoomJoinsInfo(reqVO.getRoomId(), thirdId);

        SignAddReqVO signAddReqVO = new SignAddReqVO();
        signAddReqVO.setCreatorId(String.valueOf(thirdId));
        signAddReqVO.setRoomId(reqVO.getRoomId());
        signAddReqVO.setCreatorNickname(info.getNickname());
        signAddReqVO.setCreatorAvatar(info.getAvatar());
        signAddReqVO.setShowTime(reqVO.getShowTime());
        signAddReqVO.setAppId(appId);

        //从共享服务获取返回值
        Sign sign = create(signAddReqVO);
        //构造body
        JSONObject json = new JSONObject();
        json.put("type", SignConstant.SIGN_TYPE);
        json.put(ROOM_ID_COLUMN, sign.getRoomId());
        json.put("sign_id", sign.getId());
        json.put("sign_show_time", sign.getShowTime());
        json.put("sign_creator_id", sign.getCreatorId());
        json.put("sign_creator_avatar", sign.getCreatorAvatar());
        json.put("sign_creator_nickname", sign.getCreatorNickname());
        String body = json.toJSONString();
        //发送消息至微吼云
        RoomsEntityDTO roomsEntityInfo = getRoomsEntityInfo(reqVO.getRoomId());
        paasService.sendMessage(roomsEntityInfo.getChannelId(), body);
        BeanUtils.copyProperties(sign, signDTO);
        return signDTO;
    }

    private Sign create(SignAddReqVO reqVo) {
        Sign sign = new Sign();
        BeanUtils.copyProperties(reqVo, sign);
        sign.setCreatedAt(new Date());
        signMapper.insert(sign);
        return sign;
    }


    /**
     * 用户签到
     *
     * @param
     * @return
     */
    @Override
    public SignRecordDTO in(SignInToVpsReqVO reqVO) {
        SignRecordDTO signRecordDTO = new SignRecordDTO();
        //由共享服务用户签到
        VssToken vssToken = VssTokenThreadLocal.getToken();
        Integer thirdPartyUserId = vssToken.getThirdPartyUserId();
        RoomJoinsEntityDTO info = roomInnerFacade.getRoomJoinInfo(thirdPartyUserId, reqVO.getRoomId());
        if (info == null) {
            throw new BusinessException(BizErrorCode.BIZ_INVALID_USER);
        }
        SignInReqVO signInReqVO = new SignInReqVO();
        signInReqVO.setSignerId(String.valueOf(thirdPartyUserId));
        signInReqVO.setRoomId(reqVO.getRoomId());
        if (StringUtils.isNotBlank(signInReqVO.getSignerId()) && StringUtils.isNotBlank(signInReqVO.getRoomId())) {
            if (StringUtils.isNotBlank(info.getNickname())) {
                signInReqVO.setSignerNickname(info.getNickname());
            }
            if (StringUtils.isNotBlank("avatar")) {
                signInReqVO.setSignerAvatar(info.getAvatar());
            }
        }
        signInReqVO.setSignId(reqVO.getSignId());
        signInReqVO.setAppId(appId);
        SignRecord signRecord = in(signInReqVO);
        String roomId = reqVO.getRoomId();
        updateIsSignedByJoinId(roomId, String.valueOf(thirdPartyUserId));
        BeanUtils.copyProperties(signRecord, signRecordDTO);
        return signRecordDTO;
    }

    private SignRecord in(SignInReqVO reqVo) {
        //验证用户是否已经签到（存在签到记录）
        //拼接redis缓存key:ssc:sign:appId_sourceId_signerId
        String signKey = genRecordCacheKey(reqVo.getAppId(), reqVo.getRoomId(), reqVo.getSignId());
        //查缓存中的签到记录
        String cache = (String) redisTemplate.opsForHash().get(signKey, reqVo.getSignerId());
        if (StringUtils.isNotEmpty(cache)) {
            throw new BusinessException(BizErrorCode.COMP_SIGN_REPEATED);
        }
        //查询签到信息
        Sign sign = signMapper.selectById(reqVo.getSignId());
        if (null == sign) {
            //没有签到信息
            throw new BusinessException(BizErrorCode.COMP_SIGN_NOT_EXIST);
        }
        //签到  延迟10s
        long time = sign.getCreatedAt().getTime() + sign.getShowTime() * 1000 + 10 * 1000;
        if (System.currentTimeMillis() > time) {
            throw new BusinessException(BizErrorCode.COMP_SIGN_TIMEOUT);
        }

        //DB再查询
        SignRecord querySignRecord = signRecordMapper.selectOne(Wrappers.<SignRecord>lambdaQuery()
                .eq(SignRecord::getSignId, reqVo.getSignId())
                .eq(SignRecord::getSignerId, reqVo.getSignerId())
                .isNull(SignRecord::getDeletedAt).last("limit 0, 1"));
        if (querySignRecord != null) {
            //设置缓存，有效期1天
            redisTemplate.opsForHash().put(signKey, reqVo.getSignerId(), JSON.toJSONString(querySignRecord));
            redisTemplate.expire(signKey, DEFAULT_VALUE_EXPIRED_TIME, TimeUnit.SECONDS);
            return querySignRecord;
        }


        SignRecord signRecord = new SignRecord();
        signRecord.setAppId(sign.getAppId());
        signRecord.setRoomId(sign.getRoomId());
        signRecord.setCreatorId(sign.getCreatorId());
        signRecord.setSignId(reqVo.getSignId());
        signRecord.setSignerId(reqVo.getSignerId());
        if (Strings.isNotEmpty(reqVo.getSignerNickname())) {
            signRecord.setSignerNickname(reqVo.getSignerNickname());
        }
        if (Strings.isNotEmpty(reqVo.getSignerAvatar())) {
            signRecord.setSignerAvatar(reqVo.getSignerAvatar());
        }
        signRecord.setCreatedAt(new Date());
        if (signRecordMapper.insert(signRecord) > 0) {
            //设置缓存，有效期1天
            redisTemplate.opsForHash().put(signKey, reqVo.getSignerId(), JSON.toJSONString(signRecord));
            redisTemplate.expire(signKey, DEFAULT_VALUE_EXPIRED_TIME, TimeUnit.SECONDS);
            return signRecord;
        }
        throw new BusinessException(GlobalErrorCode.FAILED);
    }

    private String genRecordCacheKey(String appId, String sourceId, Long signId) {
        sourceId = StringUtils.isEmpty(sourceId) ? "" : sourceId;
        String signIdStr = signId == null ? "" : signId.toString();
        return RedisKey.SIGN_KEY_PREFIX + appId + ":" + sourceId + ":" + signIdStr;
    }

    /**
     * 根据加入Id更新签到状态
     *
     * @param roomId
     * @param thirdPartyUserId
     */
    private void updateIsSignedByJoinId(String roomId, String thirdPartyUserId) {
        roomInnerFacade.updateIsSignedByJoinId(roomId, thirdPartyUserId);
    }

    /**
     * 获取签到记录列表
     *
     * @param
     * @return
     */
    @Override
    public SignRecordGetsRspVO recordsGet(SignRecordsToVpsReqVO reqVO) {
        //由共享服务获取签到记录列表
        VssToken vssToken = VssTokenThreadLocal.getToken();
        String thirdPartyUserId = String.valueOf(vssToken.getThirdPartyUserId());
        SignRecordGetsReqVO signRecordGetsReqVO = new SignRecordGetsReqVO();
        signRecordGetsReqVO.setCreatorId(thirdPartyUserId);
        if (!"all".equals(reqVO.getType())) {
            signRecordGetsReqVO.setRoomId(reqVO.getRoomId());
        }
        if (null != reqVO.getSignId()) {
            signRecordGetsReqVO.setSignId(reqVO.getSignId());
        }
        if (reqVO.getOffset() != null) {
            signRecordGetsReqVO.setOffset(reqVO.getOffset());
        }
        if (reqVO.getLimit() != null) {
            signRecordGetsReqVO.setLimit(reqVO.getLimit());
        }
        if (StringUtils.isNotBlank(reqVO.getSortType())) {
            signRecordGetsReqVO.setSortType(reqVO.getSortType());
        }
        signRecordGetsReqVO.setAppId(appId);

        return listRecord(signRecordGetsReqVO);
    }

    private SignRecordGetsRspVO listRecord(SignRecordGetsReqVO reqVo) {
        QueryWrapper<SignRecord> where = Wrappers.<SignRecord>query()
                .eq("app_id", reqVo.getAppId())
                .eq(StringUtils.isNotBlank(reqVo.getCreatorId()), "creator_id", reqVo.getCreatorId())
                .eq(StringUtils.isNotBlank(reqVo.getRoomId()), ROOM_ID_COLUMN, reqVo.getRoomId())
                .eq(reqVo.getSignId() != null, "sign_id", reqVo.getSignId())
                .eq(StringUtils.isNotBlank(reqVo.getSignerId()), "signer_id", reqVo.getSignerId());
        if (reqVo.getOffset() == null) {
            reqVo.setOffset(0);
        }
        if (reqVo.getLimit() == null) {
            reqVo.setLimit(10);
        }
        if (StringUtils.isBlank(reqVo.getSortType()) || !StringUtils.equalsAnyIgnoreCase(reqVo.getSortType(), "asc", "desc")) {
            reqVo.setSortType("ASC");
        }
        SignRecordGetsRspVO rspVo = new SignRecordGetsRspVO();
        Integer count = signRecordMapper.selectCount(where);
        if (count > 0) {
            //排序字段和顺序
            where.orderBy(true, "asc".equalsIgnoreCase(reqVo.getSortType()), CREATED_AT_COLUMN);
            //offset和limit
            where.last("limit " + reqVo.getOffset() + ", " + reqVo.getLimit());
            List<SignRecord> signRecords = signRecordMapper.selectList(where);
            BeanUtils.copyProperties(reqVo, rspVo);
            rspVo.setList(signRecords.stream().map(signRecord -> {
                SignRecordVOForList signRecordVoForList = new SignRecordVOForList();
                BeanUtils.copyProperties(signRecord, signRecordVoForList);
                return signRecordVoForList;
            }).collect(Collectors.toList()));
        } else {
            rspVo.setList(Lists.newArrayList());
        }
        rspVo.setCount(count);
        return rspVo;
    }

    /**
     * 获取发送的签到列表
     *
     * @param
     * @return
     */
    @Override
    public SignGetsRspVO gets(SignGetToVpsReqVO reqVO) {
        //由共享服务获取签到记录列表
        VssToken vssToken = VssTokenThreadLocal.getToken();
        String thirdPartyUserId = String.valueOf(vssToken.getThirdPartyUserId());

        SignGetsReqVO signGetsReqVO = new SignGetsReqVO();
        signGetsReqVO.setCreatorId(thirdPartyUserId);
        if (!"all".equals(reqVO.getType())) {
            signGetsReqVO.setRoomId(reqVO.getRoomId());
        }
        if (reqVO.getOffset() != null) {
            signGetsReqVO.setOffset(reqVO.getOffset());
        }
        if (reqVO.getLimit() != null) {
            signGetsReqVO.setLimit(reqVO.getLimit());
        }
        if (StringUtils.isNotBlank(reqVO.getSortType())) {
            signGetsReqVO.setSortType(reqVO.getSortType());
        }
        return list(signGetsReqVO);
    }

    private RoomJoinsEntityDTO getRoomJoinsInfo(String roomId, Integer accountId) {
        return roomInnerFacade.getRoomJoinsByRoomIdAndThirdId(roomId, accountId);
    }

    private RoomsEntityDTO getRoomsEntityInfo(String roomId) {
        return roomInnerFacade.getRoomsEntityInfo(roomId);

    }

    public SignGetsRspVO list(SignGetsReqVO reqVo) {
        QueryWrapper<Sign> where = Wrappers.<Sign>query()
                .eq(StringUtils.isNotBlank(reqVo.getAppId()), "app_id", reqVo.getAppId())
                .eq(StringUtils.isNotBlank(reqVo.getCreatorId()), "creator_id", reqVo.getCreatorId())
                .eq(StringUtils.isNotBlank(reqVo.getRoomId()), ROOM_ID_COLUMN, reqVo.getRoomId())
                .ge(StringUtils.isNotBlank(reqVo.getStartTime()), CREATED_AT_COLUMN, reqVo.getStartTime())
                .le(StringUtils.isNotBlank(reqVo.getEndTime()), CREATED_AT_COLUMN, reqVo.getEndTime());
        QueryWrapper<Sign> whereTime = Wrappers.<Sign>query()
                .ge(StringUtils.isNotBlank(reqVo.getStartTime()), CREATED_AT_COLUMN, reqVo.getStartTime())
                .le(StringUtils.isNotBlank(reqVo.getEndTime()), CREATED_AT_COLUMN, reqVo.getEndTime());
        Integer countTime = signMapper.selectCount(whereTime);
        if (countTime == 0) {
            throw new BusinessException(BizErrorCode.BIZ_INVALID_PARAM);
        }
        if (reqVo.getOffset() == null) {
            reqVo.setOffset(0);
        }
        if (reqVo.getLimit() == null) {
            reqVo.setLimit(10);
        }
        if (StringUtils.isBlank(reqVo.getSortType()) || !StringUtils.equalsAnyIgnoreCase(reqVo.getSortType(), "asc", "desc")) {
            reqVo.setSortType("ASC");
        }
        SignGetsRspVO rspVo = new SignGetsRspVO();
        Integer count = signMapper.selectCount(where);
        if (count > 0) {
            //排序字段和顺序
            where.orderBy(true, "asc".equalsIgnoreCase(reqVo.getSortType()), CREATED_AT_COLUMN);
            //offset和limit
            where.last("limit " + reqVo.getOffset() + ", " + reqVo.getLimit());
            List<Sign> signs = signMapper.selectList(where);
            BeanUtils.copyProperties(reqVo, rspVo);
            rspVo.setSortType(reqVo.getSortType());
            List<SignVOForList> list = signs.stream().map(sign -> {
                SignVOForList signVoForList = new SignVOForList();
                BeanUtils.copyProperties(sign, signVoForList);
                return signVoForList;
            }).collect(Collectors.toList());
            if (reqVo.getGetSignCount() != null && reqVo.getGetSignCount() == 1) {
                //获取此签到的签到人数
                list.forEach(item -> {
                    Integer signCount = signRecordMapper.distinctStat(item.getAppId(), item.getRoomId(), item.getId());
                    item.setSignCount(signCount);
                });
            }
            rspVo.setList(list);
        } else {
            rspVo.setList(Lists.newArrayList());
        }
        rspVo.setCount(count);
        return rspVo;
    }

}
